package com.lx.wrapper;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.lx.constant.ano.FieldFunction;
import com.lx.constant.consts.CommonConstant;
import com.lx.constant.enums.ConditionKeyWordEnum;
import com.lx.constant.enums.PreConditionTypeEnum;
import com.lx.constant.enums.SegmentEnum;
import com.lx.constant.execption.LambdaIllegalParamException;
import com.lx.constant.others.limit.BasePage;
import com.lx.constant.segment.Segment;
import com.lx.constant.segment.common.ConditionSegment;
import com.lx.constant.segment.common.LimitSegment;
import com.lx.inter.condition.ICommonDefineCondition;

import java.lang.reflect.Field;

/**
 * * S/HQL语句公共抽象条件类
 * * 用途：
 * * 1.根据组合条件生成HQL语句
 * * 2.根据组合条件生成SQL语句
 *
 * @author lx
 * @create 2023/2/25 14:48
 **/
public abstract class AbstractCommonConditionWrapper<MainEntity, ChildWrapper extends AbstractWrapper<MainEntity, ChildWrapper>> extends AbstractWrapper<MainEntity, ChildWrapper> implements ICommonDefineCondition<ChildWrapper> {

    public AbstractCommonConditionWrapper(Class<MainEntity> entityClass) {
        super(entityClass);
    }

    /**
     * 前置条件类型
     */
    private volatile PreConditionTypeEnum preConditionType = PreConditionTypeEnum.WHERE;

    /**
     * 设置或切换前置条件
     *
     * @return
     */
    public ChildWrapper where() {
        setPreConditionType(PreConditionTypeEnum.WHERE);
        return (ChildWrapper) this;
    }

    @Override
    public ChildWrapper and() {
        return addCondition(null, ConditionKeyWordEnum.AND);
    }

    @Override
    public ChildWrapper and(AbstractCommonConditionWrapper childWrapper) {
        return addCondition(null, ConditionKeyWordEnum.AND, childWrapper);
    }

    @Override
    public ChildWrapper not() {
        return addCondition(null, ConditionKeyWordEnum.NOT);
    }

    @Override
    public ChildWrapper or() {
        return or(null);
    }

    @Override
    public ChildWrapper or(AbstractCommonConditionWrapper childWrapper) {
        return addCondition(null, ConditionKeyWordEnum.OR, childWrapper);
    }

    /**
     * 分页 Limit 限制条件
     * <p>
     * 注意:Limit关键字在HQL语句中无法适用于子查询,如遇存在子查询的数据,请使用SQL语句
     *
     * @param pageSize 查询条数
     * @return
     */
    public ChildWrapper limit(Integer pageSize) {
        return limit(0, pageSize);
    }

    /**
     * * 分页 Limit 限制条件
     * * <p>
     * * 注意:Limit关键字在HQL语句中无法适用于子查询,如遇存在子查询的数据,请使用SQL语句
     *
     * @param currentPage 当前页数
     * @param pageSize    每页大小
     * @return
     */
    public ChildWrapper limit(Integer currentPage, Integer pageSize) {
        return limit(new BasePage(currentPage, pageSize));
    }

    /**
     * 分页 Limit 限制条件
     * <p>
     * 注意:Limit关键字在HQL语句中无法适用于子查询,如遇存在子查询的数据,请使用SQL语句
     *
     * @param page 分页对象
     * @return
     */
    public ChildWrapper limit(BasePage page) {
        if (page == null) {
            return (ChildWrapper) this;
        }
        //校验Limit个人,仅能保存一个
        LimitSegment limitSegment = replaceOrAddSegment(new LimitSegment(), SegmentEnum.LIMIT, (Field) null, null);

        limitSegment.setCurrentSize(page.getCurrentSize());
        limitSegment.setPageSize(page.getPageSize());
        return (ChildWrapper) this;
    }

    /**
     * 设置前置条件类型
     *
     * @param preConditionType
     * @return
     */
    protected ChildWrapper setPreConditionType(PreConditionTypeEnum preConditionType) {
        this.preConditionType = preConditionType;
        return (ChildWrapper) this;
    }

    protected PreConditionTypeEnum getPreConditionType() {
        return this.preConditionType;
    }

    /**
     * 添加条件公共方法
     *
     * @param columnColFn
     * @param conditionKeyWordEnum
     * @param valArr
     * @param <EveryEntity>
     * @return
     */
    protected final <EveryEntity> ChildWrapper addCondition(FieldFunction<EveryEntity, ?> columnColFn, ConditionKeyWordEnum conditionKeyWordEnum, Object... valArr) {
        return addCondition(null, columnColFn, conditionKeyWordEnum, null, null, valArr);
    }

    /**
     * 添加条件公共方法
     *
     * @param columnColFn
     * @param conditionKeyWordEnum
     * @param conditionFieldFn
     * @param <EveryEntity>
     * @param <OtherEntity>
     * @return
     */
    protected final <EveryEntity, OtherEntity> ChildWrapper addCondition(FieldFunction<EveryEntity, ?> columnColFn, ConditionKeyWordEnum conditionKeyWordEnum, FieldFunction<OtherEntity, ?> conditionFieldFn) {
        return addCondition(null, columnColFn, conditionKeyWordEnum, null, conditionFieldFn);
    }

    /**
     * 添加条件公共方法
     *
     * @param columnColFn          条件左列列名对象
     * @param conditionKeyWordEnum 条件类型
     * @param childWrapper         子查询项
     * @param <EveryEntity>        实体类型
     * @return
     */
    protected final <EveryEntity> ChildWrapper addCondition(FieldFunction<EveryEntity, ?> columnColFn, ConditionKeyWordEnum conditionKeyWordEnum, AbstractCommonConditionWrapper childWrapper) {
        return addCondition(null, columnColFn, conditionKeyWordEnum, childWrapper, null);
    }

    /**
     * 添加条件公共方法
     *
     * @param columnColFn          条件列项
     * @param conditionKeyWordEnum 条件类型
     * @param childWrapper         子查询项
     * @param conditionFieldFn     查询属性项
     * @param valArr               查询值项
     * @param <EveryEntity>
     * @param <OtherEntity>
     * @return
     */
    protected final <EveryEntity, OtherEntity> ChildWrapper addCondition(String columnName, FieldFunction<EveryEntity, ?> columnColFn, ConditionKeyWordEnum conditionKeyWordEnum,
                                                                         AbstractCommonConditionWrapper childWrapper, FieldFunction<OtherEntity, ?> conditionFieldFn, Object... valArr) {
        return addCondition(null, columnName, columnColFn, conditionKeyWordEnum, childWrapper, conditionFieldFn, valArr);
    }

    /**
     * 添加条件公共方法
     *
     * @param columnColFn          条件列项
     * @param conditionKeyWordEnum 条件类型
     * @param childWrapper         子查询项
     * @param conditionFieldFn     查询属性项
     * @param valArr               查询值项
     * @param <EveryEntity>
     * @param <OtherEntity>
     * @return
     */
    protected final <EveryEntity, OtherEntity> ChildWrapper addCondition(PreConditionTypeEnum preConditionType, String columnName, FieldFunction<EveryEntity, ?> columnColFn, ConditionKeyWordEnum conditionKeyWordEnum,
                                                                         AbstractCommonConditionWrapper childWrapper, FieldFunction<OtherEntity, ?> conditionFieldFn, Object... valArr) {
        if (conditionKeyWordEnum.getGrade() != 0) {
            checkParamIsValid(conditionKeyWordEnum, "SQL构建时,传入列属性或子条件项非法!", columnColFn, childWrapper, columnName);
        }
        //根据前置条件获取语句类型
        SegmentEnum segmentType = (preConditionType == null ? this.preConditionType : preConditionType).getConditionSegment();

        ConditionSegment segmentNode = createAndAddSegment(new ConditionSegment(), segmentType, columnColFn, columnName);

        //设置条件语句特殊属性.
        segmentNode.setConditionKeyWord(conditionKeyWordEnum);
        segmentNode.setColumnValue(valArr);
        segmentNode.setChildConditionWrapper(childWrapper);

        //当查询值项为实体列明属性时,进行记录
        Field conditionField = getColumnFieldByFn(conditionFieldFn);
        if (conditionField != null) {
            segmentNode.setConditionEntityClass(conditionField.getDeclaringClass());
            segmentNode.setConditionField(conditionField);
        }

        return (ChildWrapper) this;
    }


    protected String buildConditions(boolean isHQL, AbstractWrapper parentWrapper, SegmentEnum segmentEnum) {
        return buildConditions(isHQL, parentWrapper, segmentEnum, null);
    }

    /**
     * 通用:构建[{@link Segment}]条件S/HQL
     *
     * @param isHQL            是否为HQL条件语句构建
     * @param parentWrapper    父类Wrapper
     * @param segmentEnum
     * @param defaultCondition 默认条件
     * @return
     */
    protected String buildConditions(boolean isHQL, AbstractWrapper parentWrapper, SegmentEnum segmentEnum, Object defaultCondition) {
        StringBuilder conditionBuilder = new StringBuilder(128);

        //生成QL语句
        String segmentQL = this.buildSegmentQL(isHQL, parentWrapper, segmentEnum);

        boolean sgIsNull = StrUtil.isEmpty(segmentQL);
        if (sgIsNull && defaultCondition == null) {
            return CommonConstant.EMPTY;
        }
        return conditionBuilder.append(segmentEnum.getKeyWord()).append(sgIsNull ? defaultCondition : segmentQL).toString();
    }

    /**
     * 尝试获取QL语句拼接片段
     *
     * @param isHQL         是否为HQL
     * @param segmentEnum   获取组成语句类型枚举
     * @param parentWrapper 父类Wrapper
     * @return
     */
    public String tryGetQLFragment(boolean isHQL, SegmentEnum segmentEnum, AbstractWrapper parentWrapper) {
        return tryGetQLFragment(isHQL, false, segmentEnum, parentWrapper);
    }

    /**
     * 尝试获取条件语句字符串
     *
     * @param isHQL         是否为HQL
     * @param needPrefix    是否需要组成语句的前缀
     * @param segmentEnum   获取组成语句类型枚举
     * @param parentWrapper 父类Wrapper
     * @return
     */
    public String tryGetQLFragment(boolean isHQL, boolean needPrefix, SegmentEnum segmentEnum, AbstractWrapper parentWrapper) {
        StringBuilder conditionBuilder = new StringBuilder(128);
        //生成QL语句
        String segmentQL = this.buildSegmentQL(isHQL, parentWrapper, segmentEnum);

        boolean sgIsNull = StrUtil.isEmpty(segmentQL);
        if (sgIsNull) {
            return CommonConstant.EMPTY;
        }
        return conditionBuilder.append(needPrefix ? segmentEnum.getKeyWord() : CommonConstant.EMPTY).append(segmentQL).toString();
    }

    /**
     * 检查传入参数是否为空
     *
     * @param wordEnum
     * @param params
     */
    protected void checkParamIsValid(ConditionKeyWordEnum wordEnum, Object... params) {
        checkParamIsValid(wordEnum, "传入参数不能为空!", params);
    }

    /**
     * 检查传入参数是否为空
     *
     * @param wordEnum
     * @param params
     */
    protected void checkParamIsValid(ConditionKeyWordEnum wordEnum, String errorMsg, Object... params) {
        if (ArrayUtil.isEmpty(params)) {
            throw new LambdaIllegalParamException("设置语句类型=[" + wordEnum.name() + "]时," + errorMsg);
        }
    }

}
